// CS 362 - Assignment #5
// Written by Tim Chen
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "dominion.h"
#include "interface.h"
#include "dominion_helpers.h"
#include "rngs.h"
#include "math.h"
#include "time.h"

int actionPriority[10];
int buyPriority[15];

// implicit declaration resolution
int playActionCard(struct gameState *state, int player, int card, int handPos);

/*
DEVELOP A PLAN & STRATEGY.  EXECUTE FOR BUILDING A GAME.

GOAL: Play complete games of dominion with:
-A random number of players (2-4)
-A random set of kingdom cards.

*Use interface to help print key info like game state
to gameResults.out.
*testdominion.out contains results of tests such as whether 
each test case passes and how many test cases were run.
(TEST A FULL GAME AND GAME CONDITIONS)

*/

void makeRandomKingdomCards(struct gameState *state, int kingdom_cards[]){
	/*
	 * Objective: Pick 10 random kingdom cards and removing them from selection if already picked.
	 * According to dominion.h, the following kingdom cards are available (20 total):
	 *	-adventurer, council_room, feast, gardens, mine, remodel, smithy, village
	 *  -baron, great_hall, minion, steward, tribute, ambassador, cutpurse, embargo
	 *  -outpost, salvager, sea_hag, treasure_map
	 */
	 int i = 0;
	 int pickRandom = 0;
	 int pickKingdom = 0;
	 int kingdomCardCount = 0;
	 
	srand(time(0)); // seed a random number via time

	 while(kingdomCardCount < 11){
	 	// grab a random kingdom card
	 	pickRandom = rand() % 21;
	 	//printf("Random #: %d\n", pickRandom);
	 	switch(pickRandom){
	 		case 1: // adventurer
	 			pickKingdom = adventurer;
	 			break;
	 		case 2: // council room
	 			pickKingdom = council_room;
	 			break;
	 		case 3: // feast
	 			pickKingdom = feast;
	 			break;
 			case 4: // gardens
 				pickKingdom = gardens;
 				break;
			case 5: // mine
				pickKingdom = mine;
				break;
			case 6: // remodel
				pickKingdom = remodel;
				break;
			case 7: // smithy
				pickKingdom = smithy;
				break;
			case 8: // village
				pickKingdom = village;
				break;
			case 9: // baron
				pickKingdom = baron;
				break;
			case 10: // great hall
				pickKingdom = great_hall;
				break;
			case 11: // minion
				pickKingdom = minion;
				break;
			case 12: // steward
				pickKingdom = steward;
				break;
			case 13: // tribute
				pickKingdom = tribute;
				break;
			case 14: // ambassador
				pickKingdom = ambassador;
				break;
			case 15: // cutpurse
				pickKingdom = cutpurse;
				break;
			case 16: // embargo
				pickKingdom = embargo;
				break;
			case 17: // outpost
				pickKingdom = outpost;
				break;
			case 18: // salvager
				pickKingdom = salvager;
				break;
			case 19: // sea hag
				pickKingdom = sea_hag;
				break;
			case 20: // treasure map
				pickKingdom = treasure_map;
				break;
	 		default:
	 			break;
	 	}

	 	// check that card has not been selected
	 	if(kingdomCardCount == 0){
	 		kingdom_cards[0] = pickKingdom;
	 		kingdomCardCount++;
	 		//printf("KINGDOM CARD #1: %d. Kingdom Card Count: %d\n", kingdom_cards[0], kingdomCardCount);
	 	}else{
	 		//printf("Current Kingdom Card Count: %d\n", kingdomCardCount);
		 	for(i = 0; i < kingdomCardCount; i++){
		 		if(kingdom_cards[i] == pickKingdom){
		 			//printf("ALREADY PICKED %d: %d. Kingdom Card Count: %d\n", i, pickKingdom, kingdomCardCount);
		 			kingdomCardCount--; // decrement to ensure we pick another card
		 			break;
		 		}else if(i == kingdomCardCount - 1){
		 			kingdom_cards[i] = pickKingdom;
		 			//printf("KINGDOM CARD #%d: %d. Kingdom Card Count: %d\n", i+1, kingdom_cards[i], kingdomCardCount);
		 		}
		 	}
		 	kingdomCardCount++;
		}
	 	//printf("Kingdom Card Count: %d\n", kingdomCardCount);
	 }
	 //return kingdom_cards[];
}

int makeRandomNumPlayers(struct gameState *state, int numPlayers){
	int numPlayerSeed = 5;
	srand(time(0)); // seed a random number via time
	while(numPlayers < 2 || numPlayers > 4){
		numPlayers = rand() % numPlayerSeed;
	}
	return numPlayers;
}

int makeRandomSeed(int seed){
	srand(time(0));
	seed = rand();
	return seed;
}

// Objective of this function is to help decide what cards to focus play on
void makeRandomPlayPriorities(int ten_cards[]){
	int i = 0, j = 0;
	int actionCardCount = 0;
	int buyCardCount = 0;
	int pickKingdom = 0;
	int pickCard = 0; // used on the buy card list
	int pickRandom = 0;
	int count = 0;
	int allActionPossibilities[19];
	int allBuyPossibilities[24]; // excluding curse & copper for reasonable strategy purposes
	int fifteen_cards[15];
	char cardName[MAX_STRING_LENGTH];
	int randSeed = 0;

	srand(time(0));

	// Generate actionPriority, which is limited to kingdom cards only
	while(actionCardCount < 21){
	 	// extra randomization to ensure priority doesn't match kingdom card selection
	 	randSeed = rand() % 10;
	 	pickRandom = randSeed*rand() % 21;
	 	//printf("Random #: %d\n", pickRandom);
	 	switch(pickRandom){
	 		case 1: // adventurer
	 			pickKingdom = adventurer;
	 			break;
	 		case 2: // council room
	 			pickKingdom = council_room;
	 			break;
	 		case 3: // feast
	 			pickKingdom = feast;
	 			break;
 			case 4: // gardens
 				pickKingdom = gardens;
 				break;
			case 5: // mine
				pickKingdom = mine;
				break;
			case 6: // remodel
				pickKingdom = remodel;
				break;
			case 7: // smithy
				pickKingdom = smithy;
				break;
			case 8: // village
				pickKingdom = village;
				break;
			case 9: // baron
				pickKingdom = baron;
				break;
			case 10: // great hall
				pickKingdom = great_hall;
				break;
			case 11: // minion
				pickKingdom = minion;
				break;
			case 12: // steward
				pickKingdom = steward;
				break;
			case 13: // tribute
				pickKingdom = tribute;
				break;
			case 14: // ambassador
				pickKingdom = ambassador;
				break;
			case 15: // cutpurse
				pickKingdom = cutpurse;
				break;
			case 16: // embargo
				pickKingdom = embargo;
				break;
			case 17: // outpost
				pickKingdom = outpost;
				break;
			case 18: // salvager
				pickKingdom = salvager;
				break;
			case 19: // sea hag
				pickKingdom = sea_hag;
				break;
			case 20: // treasure map
				pickKingdom = treasure_map;
				break;
	 		default:
	 			break;
	 	}

	 	if(actionCardCount == 0){
	 		allActionPossibilities[0] = pickKingdom;
	 		actionCardCount++;
	 	}else{
	 		for(i = 0; i < actionCardCount; i++){
		 		if(allActionPossibilities[i] == pickKingdom){
		 			actionCardCount--; // decrement to ensure we pick another card
		 			break;
		 		}else if(i == actionCardCount - 1){
		 			allActionPossibilities[i] = pickKingdom;
		 		}
		 	}
		 	actionCardCount++;
		}
	 }

	 printf("\nAction Card Priority is as follows: \n");
	 for(i = 0; i < actionCardCount - 1; i++){
	 	cardNumToName(allActionPossibilities[i], cardName);
	 	printf("%d: %s\n", i+1, cardName);
	 }

	// Build actionPriority based on kingdom cards
	count = 0;
	for(i = 0; i < 20; i++){ // iterate through all possible action cards
		for(j = 0; j < 10; j++){ // iterate through selected kingdom cards
			if(allActionPossibilities[i] == ten_cards[j]){
				actionPriority[count] = allActionPossibilities[i];
				count++;
			}
		}
	}

	printf("\nAction Card Priority this game is as follows: \n");
	for(i = 0; i < 10; i++){
		cardNumToName(actionPriority[i], cardName);
		printf("%d: %s\n", i+1, cardName);
	}

	// Generate buyPriority, which is focused on kingdom cards, victory cards, and silver/gold treasure card.
	while(buyCardCount < 25){
	 	// extra randomization to ensure priority doesn't match kingdom card selection
	 	randSeed = rand() % 10;
	 	pickRandom = randSeed*rand() % 26;
	 	//printf("Random #: %d\n", pickRandom);
	 	switch(pickRandom){
	 		case 1: // adventurer
	 			pickCard = adventurer;
	 			break;
	 		case 2: // council room
	 			pickCard = council_room;
	 			break;
	 		case 3: // feast
	 			pickCard = feast;
	 			break;
 			case 4: // gardens
 				pickCard = gardens;
 				break;
			case 5: // mine
				pickCard = mine;
				break;
			case 6: // remodel
				pickCard = remodel;
				break;
			case 7: // smithy
				pickCard = smithy;
				break;
			case 8: // village
				pickCard = village;
				break;
			case 9: // baron
				pickCard = baron;
				break;
			case 10: // great hall
				pickCard = great_hall;
				break;
			case 11: // minion
				pickCard = minion;
				break;
			case 12: // steward
				pickCard = steward;
				break;
			case 13: // tribute
				pickCard = tribute;
				break;
			case 14: // ambassador
				pickCard = ambassador;
				break;
			case 15: // cutpurse
				pickCard = cutpurse;
				break;
			case 16: // embargo
				pickCard = embargo;
				break;
			case 17: // outpost
				pickCard = outpost;
				break;
			case 18: // salvager
				pickCard = salvager;
				break;
			case 19: // sea hag
				pickCard = sea_hag;
				break;
			case 20: // treasure map
				pickCard = treasure_map;
				break;
			case 21: // estate
				pickCard = estate;
				break;
			case 22: // duchy
				pickCard = duchy;
				break;
			case 23: // province
				pickCard = province;
				break;
			case 24: // silver
				pickCard = silver;
				break;
			case 25: // gold
				pickCard = gold;
				break;
	 		default:
	 			break;
	 	}

	 	if(buyCardCount == 0){
	 		allBuyPossibilities[0] = pickCard;
	 		buyCardCount++;
	 	}else{
	 		for(i = 0; i < buyCardCount; i++){
		 		if(allBuyPossibilities[i] == pickCard){
		 			buyCardCount--; // decrement to ensure we pick another card
		 			break;
		 		}else if(i == buyCardCount - 1){
		 			allBuyPossibilities[i] = pickCard;
		 		}
		 	}
		 	buyCardCount++;
		}
	 }

	 printf("\nBuy Card Priority is as follows: \n");
	 for(i = 0; i < buyCardCount - 1; i++){
	 	cardNumToName(allBuyPossibilities[i], cardName);
	 	printf("%d: %s\n", i+1, cardName);
	 }

	 // Create a hybrid of kingdom cards & the 5 extra options
	 for(i = 0; i < 10; i++){
	 	fifteen_cards[i] = ten_cards[i];
	 }
	 // Manually add the 5 missing options
	 fifteen_cards[10] = province;
	 fifteen_cards[11] = silver;
	 fifteen_cards[12] = duchy;
	 fifteen_cards[13] = gold;
	 fifteen_cards[14] = estate;

	 printf("\n15 Card Check is as follows: \n");
	 for(i = 0; i < 15; i++){
	 	cardNumToName(fifteen_cards[i], cardName);
	 	printf("%d: %s\n", i+1, cardName);
	 }

	// Build buyPriority based on 15 cards
	 /*
	count = 0;
	for(i = 0; i < 24; i++){ // iterate through all possible buy cards
		for(j = 0; j < 15; j++){ // iterate through the fifteen cards
			if(allBuyPossibilities[i] == fifteen_cards[j]){
				buyPriority[count] = allBuyPossibilities[i];
				//cardNumToName(buyPriority[count], cardName);
				//printf("Count: %d; BuyPossible: %d; FifteenCard: %d; Added %s\n", count, i, j, cardName);
				count++;
			}
		}
	}
	
	if(buyPriority[14] == curse){
		printf("BUG FOUND: curse card appeared\n");
	}*/
	/*
	!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	 Due to bugs, fifteen_cards is automatically buy priority via randomness
	!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	*/
	printf("\nBuy Card Priority this game is as follows: \n");
	for(i = 0; i < 15; i++){
		buyPriority[i] = fifteen_cards[i];
		cardNumToName(buyPriority[i], cardName);
		printf("%d: %s\n", i+1, cardName);
	}
}

// This function serves as the AI
void makeMove(struct gameState *state, int player){
	int i = 0, j = 0;
	int pickActionCard = -1;
	int pickBuyCard = -1;
	int handPosition = -1;
	int playResult = 0;
	int buyResult = -1;
	char cardName[MAX_STRING_LENGTH];
	// Action Phase
	printf("Player %d begin ACTION PHASE.  Actions available: %d\n", player, state->numActions);
	while(state->numActions > 0){
		for(i = 0; i < state->handCount[player]; i++){ // Loop through players hand
			for(j = 0; j < 10; j++){ // Loop through playable action cards
				if(state->hand[player][i] == actionPriority[j]){ // If player has card
					handPosition = i; // grab hand index to play card
					pickActionCard = state->hand[player][i];
					cardNumToName(pickActionCard, cardName);
					printf("Found Action Card: %s\n", cardName);
					break;
				}
			}
		}

		if(pickActionCard == -1){ // if no playable action cards
			break;
		}else{
			printf("Playing %s card.\n", cardName);
			playResult = playActionCard(state, player, pickActionCard, handPosition);
			if(playResult == -1){
				printf("ERROR: Could not play %s card.  Action wasted.\n", cardName);
			}
		}
		// reset values
		pickActionCard = -1;
		handPosition = -1;
	}
	printf("Player %d ACTION PHASE complete!\n", player);
	printHand(player, state);

	// Buy Phase
	printf("Player %d begin BUY PHASE. Buys available: %d\n", player, state->numBuys);
	while(state->numBuys > 0){
		for(i = 0; i < 15; i++){
			// if we have enough coins to buy the top priority card, buy it.
			// there should be cards available as well
			if(state->coins >= getCost(buyPriority[i]) && supplyCount(buyPriority[i], state) > 0){
				pickBuyCard = buyPriority[i];
			}
		}
		if(pickBuyCard > -1){
			buyResult = buyCard(pickBuyCard, state);
			if(buyResult == 0){
				cardNumToName(pickBuyCard, cardName);
				printf("Player %d will buy: %s card.\n", player, cardName);
			}

		}else{
			printf("Nothing purchased.\n");
			break; // no cards to buy
		}
		pickBuyCard = -1;
	}

}

int playActionCard(struct gameState *state, int player, int card, int handPos){
	int i = 0;
	int choice1 = -1;
	int choice2 = -1;
	int choice3 = -1;
	int cardCost = 0;
	int randomChoice = -1;
	int inventoryCheck = 0;
	char cardName[MAX_STRING_LENGTH];

	// Process through all possible action cards
	switch(card){
		case adventurer:
			playCard(handPos, 0, 0, 0, state);
			break;
		case council_room:
			playCard(handPos, 0, 0, 0, state);
			break;
		case feast:
			// Feast is used to buy a card costing up to 5 coins.
			// choice1 is the supply # of card desired/gained.
			// choice will be based on buyPriority
			for(i = 0; i < 15; i++){
				if(getCost(buyPriority[i]) < 6){
					choice1 = buyPriority[i];
				}
			}
			if(choice1 > -1){
				playCard(handPos, choice1, 0, 0, state);
			}else{
				return -1;
			}
			break;
		case gardens:
			// Gardens is not a playable card, but can still be selected.
			playCard(handPos, 0, 0, 0, state);
			break;
		case mine:
			// Mine is a trash for benefit card focused on:
			// 	-Trashing copper to gain silver.
			// 	-Trashing silver to gain gold.
			// choice1 is hand index of money to trash.
			// choice2 is supply card # (for silver & gold)
			for(i = 0; i < state->handCount[player]; i++){
				if(state->hand[player][i] == copper){
					choice1 = i; // provide hand position of copper card
					choice2 = silver; // plan to add a silver
				}else if(state->hand[player][i] == silver){
					choice1 = i; // proivde hand position of silver card
					choice2 = gold; // plan to add a gold
				}
			}

			if(choice1 > -1){ // do we have a copper to replace?
				playCard(handPos, choice1, choice2, 0, state);
			}else{
				return -1; // no treasure cards to select, card was burned
			}

			// do we need to trash the card?  dominion should handle this.
			break;
		case remodel:
			// choice1 is hand# of card to remodel (a card to trash that's not the remodel played)
			// choice2 is the supply # for card gained

			// Trash the lowest costing card from hand:
			for(i = 0; i < state->handCount[player]; i++){
				if(i == handPos){
					break; // cannot discard current remodel card
				}else{
					if(cardCost < getCost(state->hand[player][i])){
						cardCost = getCost(state->hand[player][i]);
						choice1 = i; // choice1 is the hand index #
					}
				}
			}

			// Identify a card to purchase that is trashed cost +2
			for(i = 0; i < 24; i++){
				// if the trashed card + 2 can cover the priority card, pick it!
				if(cardCost + 2 >= getCost(buyPriority[i])){
					choice2 = buyPriority[i];
				}
			}
			playCard(handPos, choice1, choice2, 0, state);
			break;
		case smithy:
			playCard(handPos, 0, 0, 0, state);
			break;
		case village:
			playCard(handPos, 0, 0, 0, state);
			break;
		case baron:
			// choice1 is boolean for discard of estate
			// cardEffect already handles the boolean cases
			choice1 = -1;

			// Determine if estate is present
			for(i = 0; i < state->handCount[player]; i++){
				if(state->hand[player][i] == estate){
					choice1 = 1;
				}
			}
			playCard(handPos, choice1, 0, 0, state);
			break;
		case great_hall:
			playCard(handPos, 0, 0, 0, state);
			break;
		case minion:
			// choice1 will add 2 coins
			// choice2 will discard hand, +4 cards, and other players with +5 cards
			//	will discard their hand & draw 4 new cards
			randomChoice = rand() % 2; // lets randomize this choice
			if(randomChoice == 0){
				choice1 = 1;
				choice2 = 0;
			}else if(randomChoice == 1){
				choice1 = 0;
				choice2 = 1;
			}else{
				return -1;
			}
			playCard(handPos, choice1, choice2, 0, state);
			break;
		case steward:
			// choice1 is to add 2 cards.
			// choice2 is to add 2 coins.
			// choice3 is to trash 2 cards.
			randomChoice = rand() % 3; // lets randomize this choice
			if(randomChoice == 0){
				choice1 = 1;
				choice2 = 0;
				choice3 = 0;
			}else if(randomChoice == 1){
				choice1 = 0;
				choice2 = 1;
				choice3 = 0;
			}else if(randomChoice == 2){
				choice1 = 0;
				choice2 = 0;
				choice3 = 1;
			}else{
				return -1;
			}
			playCard(handPos, choice1, choice2, choice3, state);
			break;
		case tribute:
			playCard(handPos, 0, 0, 0, state);
			break;
		case ambassador:
			// choice1 is the hand index #
			// choice2 is the number to return to supply
			choice1 = -1;
			choice2 = 0;

			// Attempt to find a COPPER card from hand
			for(i = 0; i < state->handCount[player]; i++){
				if(state->hand[player][i] == copper){
					choice1 = i;
					choice2++;
				}
			}
			if(choice2 > 2) choice2 = 2; // enforce cap at 2 copies to return
			playCard(handPos, choice1, choice2, 0, state); // line might be problematic
			break;
		case cutpurse:
			playCard(handPos, 0, 0, 0, state);
			break;
		case embargo:
			// choice1 is the supply #
			// play embargo on a buyPriority card
			for(i = 0; i < 15; i++){
				inventoryCheck = supplyCount(buyPriority[i], state);
				if(inventoryCheck < 1){ // don't embargo empty supply cards
					continue;
				}else{
					choice1 = buyPriority[i];
					break;
				}
			}
			playCard(handPos, choice1, 0, 0, state);
			cardNumToName(choice1, cardName);
			printf("Embargo placed on %s\n", cardName);
			break;
		case outpost:
			playCard(handPos, 0, 0, 0, state);
			break;
		case salvager:
			// choice1 is the hand index to trash
			// Strategy suggests trashing highest costing card
			for(i = 0; i < state->handCount[player]; i++){
				if(cardCost < getCost(state->hand[player][i])){
					cardCost = getCost(state->hand[player][i]);
					choice1 = i;
				}
			}
			playCard(handPos, choice1, 0, 0, state);
			break;
		case sea_hag:
			playCard(handPos, 0, 0, 0, state);
			break;
		case treasure_map:
			// FUTURE CONSIDERATION - Treasure Map should only be played
			//	if 2 treasure maps exist in hand.
			playCard(handPos, 0, 0, 0, state);
			break;
		default:
			cardNumToName(card, cardName);
			printf("ERROR: Cannot play %s card.\n", cardName);
			break;
	}
	return 0;
}

int playDominion(){
	int i = 0;
	int seed = 0;
	int numPlayers = 0;
	int activePlayer = 0;
	char cardName[MAX_STRING_LENGTH];
	struct gameState state;
	int kingdom_cards[10]; // allocation for 10 kingdom cards
	//int winner_check = 0;

	// create random kingdom cards
	makeRandomKingdomCards(&state, kingdom_cards);
	//printf("Verifying Kingdom Cards:\n");
	numPlayers = makeRandomNumPlayers(&state, numPlayers);
	//printf("Number of Players for Game: %d\n", numPlayers);

	seed = makeRandomSeed(seed);
	initializeGame(numPlayers, kingdom_cards, seed, &state);
	printf("Game Initialized with %d players and the following Kingdom cards: \n", numPlayers);
	for(i = 0; i < 10; i++){
		cardNumToName(kingdom_cards[i], cardName);
		printf("%d: %s\n", i+1, cardName);
	}
	makeRandomPlayPriorities(kingdom_cards);
	printf("BEGIN GAME\n");
	//printf("SCORE VERIFICATION\n");
	//printScores(&state);
	
	while(!isGameOver(&state)){
		activePlayer = whoseTurn(&state);
		//printf("Player %d's turn:\n", activePlayer);
		printState(&state);
		makeMove(&state, activePlayer);	//player action & buy phase
		printf("Player %d ends turn.\n", activePlayer);
		printf("SCORE UPDATE\n");
		printScores(&state);
		endTurn(&state); // cleanup phase
	}
	printf("GAME OVER!  Calculating Results\n");
	for(i = 0; i < numPlayers; i++){
		printf("Player %d score: %d\n", i+1, scoreFor(i, &state));
	}
	// get Winners has a bug!
	//winner_check = getWinners(&numPlayers, &state);
	//printf("Winner result: %d\n", winner_check);
	
	//free(&state); // cleanup game state
	//free(kingdom_cards); // cleanup kingdom cards
	return 0;
}


int main(int argc, char** argv){
	playDominion();
	return 0;
}